package cn.tedu.tmall.front.mall.service.impl;

import cn.tedu.tmall.common.ex.ServiceException;
import cn.tedu.tmall.common.pojo.CurrentUser;
import cn.tedu.tmall.common.pojo.PageData;
import cn.tedu.tmall.common.web.ServiceCodeEnum;
import cn.tedu.tmall.front.mall.dao.repository.IGoodsRepository;
import cn.tedu.tmall.front.mall.dao.repository.IOrderItemRepository;
import cn.tedu.tmall.front.mall.dao.repository.IOrderRepository;
import cn.tedu.tmall.front.mall.dao.repository.IUserAddressRepository;
import cn.tedu.tmall.front.mall.pojo.parm.GoodsItemParam;
import cn.tedu.tmall.front.mall.pojo.parm.OrderAddParam;
import cn.tedu.tmall.front.mall.pojo.parm.UserAddressParam;
import cn.tedu.tmall.front.mall.pojo.po.GoodsPO;
import cn.tedu.tmall.front.mall.pojo.po.OrderItemPO;
import cn.tedu.tmall.front.mall.pojo.po.OrderPO;
import cn.tedu.tmall.front.mall.pojo.po.UserAddressPO;
import cn.tedu.tmall.front.mall.pojo.vo.OrderGoodsItemVO;
import cn.tedu.tmall.front.mall.pojo.vo.OrderListVO;
import cn.tedu.tmall.front.mall.service.IOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OrderServiceImpl implements IOrderService {

    @Autowired
    private IGoodsRepository goodsRepository;

    @Autowired
    private IUserAddressRepository userAddressRepository;

    @Autowired
    private IOrderRepository orderRepository;

    @Autowired
    private IOrderItemRepository orderItemRepository;


    @Override
    public void createOrder(CurrentUser user, OrderAddParam orderAddParam) {
        //数量创建订单业务
        //A 订单入参
        //1 用户信息  包括 收货地址
        //2 商品信息  商品id 商品数量 商品单价
        //3 支付信息  默认 线上支付
        //4 配送信息  默认 老板跑这送
        //5 发票 营销信息 可选

        // B 业务逻辑
        // 1 验证用户收货地址 用户收货信息表 收货地址id 通过id获取具体的收货地址
        // 2 验证商品
        //   2.1 商品是否存在 是否上架,10个商品,某个商品不存在 给用户提示,创建订单
        //    (不要给用户做决定) 给用户合理的提示 比如 到购物车检查,或者继续下单
        //   2.2 计算价格(订单服务需要从新计算一遍基于商品最新信息)
        //     价格中心服务 提供接口 来计算价格  封装, 如果没有价格中心 商品可以提供
        //     核心目标是只有一个地方来计算价格,购物车/订单 来调用计算价格的接口
        // 3 可选
        // 4 库存 预售 小电商暂时不用考虑库存,用户下完单,让供应商发货
        //     酒店或者票 同一时间只能一个人定
        // C 生成订单
        //    订单表
        //      订单信息  id,(订单编号) 分布式ID生成方案 id生成器 雪花算法 订单状态(什么状态),下单时间,更新时间
        //      用户信息 用户id,用户名称,用户收货地址,联系电话
        //      商品信息 商品id,商品名称(是否需要,如果需要可以从商品服务查)商品数量 商品单价,总价 拆分成子表  1条订单数据对应多条商品信息
        //      支付信息 支付方式,支付时间,支付金额,支付状态(什么状态)
        //      配送信息 配送方式,配送时间,配送状态(什么状态)
        //      备注信息 多加肉,少放菜

        //     订单快照 用户下单的此时此刻的信息 拍了照片  要全部保存起来,不能从别的服务里关联查询

        //    1 生成订单的主表
        //       OrderPO order = new OrderPO();
        //       order 设置用户信息 ,设置支付信息,配送信息,备注信息,设置订单状态新创建
        //    2 生成商品列表 list
        //          goodsItemParams 循环 转换为 GoodsItemPO Bean.copyProperties;  计算订单总价
        //    3 保存订单数据
        //        mybaits-plus insert
        //    4 循环插入商品信息
        //        mybaits-plus insert
        //    5 注意 增加事务注解

        //1 验证商品 商品是否存在,是否上架
        List<GoodsItemParam> goodsItemParams = orderAddParam.getGoodsItemParams();
        //拿到所有商品的id
        //List<Long> goodsIds  = new ArrayList<>();

     /*   goodsItemParams.forEach(goods->{
            Long goodsId = goods.getGoodsId();
            goodsIds.add(goodsId);
        });*/

        List<Long> goodsIds = goodsItemParams.stream()
                .map(GoodsItemParam::getGoodsId).collect(Collectors.toList());


        //查询商品是否存在
        List<GoodsPO> goods = goodsRepository.listByGoodsIds(goodsIds);
        if (CollectionUtils.isEmpty(goods)) {
            throw new ServiceException(ServiceCodeEnum.GOODS_NOT_EXIST);
        }
        if (goodsIds.size() != goods.size()) {
            throw new ServiceException(ServiceCodeEnum.GOODS_NOT_EXIST);
        }
        //查询商品是否上架


      /*  goods.forEach(g->{
            if ("0".equals(g.getIsPutOn())) {
                throw new ServiceException(ServiceCodeEnum.GOODS_NOT_PUT_ON);
            }
        });*/

        List<GoodsPO> collect =
                goods.stream().filter(g -> (g.getIsPutOn() != null && 0 == g.getIsPutOn())).collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(collect)) {
            throw new ServiceException(ServiceCodeEnum.GOODS_NOT_PUT_ON);
        }

        //判断用户信息
        UserAddressParam userAddressParam = orderAddParam.getUserAddressParam();
        Integer addressId = userAddressParam.getAddressId();
        //通过这个地址id去数据库看看有没有
        //地址必须存在,并且是我的地址
        //入参的用户id和用户收货地址必须存在 , 收货地址必须属于入参的用户
        UserAddressPO userAddressPO = userAddressRepository.getAddressByUserIdAndAddressId(user.getId(), addressId);
        if (userAddressPO == null) {
            throw new ServiceException(ServiceCodeEnum.USER_ADDRESS_ERROR);
        }

        Map<Long, GoodsPO> groupGoods =
                goods.stream().collect(Collectors.toMap(GoodsPO::getId, Function.identity()));

 /*       Map<Long, GoodsPO> groupGoods1 = new HashMap<>();
        goods.forEach(goodsPO -> {
            groupGoods1.put(goodsPO.getId(),goodsPO);
        });*/

        // [PO{id,price},PO{id,price},PO {id,price}]
        // map<id,price> 3

        BigDecimal totalPrice = BigDecimal.ZERO;
        int goodsNums = 0;
        List<OrderItemPO> orderItemPOS = new ArrayList<>();
        //计算价格
        // 入参传入的商品id和商品数量
        // 通过入参的商品id查询此时此刻的商品价格
        // 商品价格 * 商品数量 = 总价
        for (GoodsItemParam goodsItemParam : goodsItemParams) {
            //入参的商品id
            Long goodsId = goodsItemParam.getGoodsId();
            //入参的商品数量
            Integer goodsNum = goodsItemParam.getGoodsNum();
            //通过入参取当前商品的信息 (价格)
            GoodsPO goodsPO = groupGoods.get(goodsId);
            BigDecimal salePrice = goodsPO.getSalePrice();
            //一种商品的单价 * 一种商品的数量 == 一种商品的总价
            BigDecimal multiply = salePrice.multiply(new BigDecimal(goodsNum));
            //一种商品的单价累加到总价上
            totalPrice = totalPrice.add(multiply);
            //一种商品的数量累加到总数量上
            goodsNums = goodsNums + goodsNum;
            //一个订单对应多个商品
            OrderItemPO orderItemPO = buildOrderItemPO(goodsNum, goodsId, salePrice);
            //BeanUtils.copyProperties(goodsPO,orderItemPO);//copy 名称不一样导致 赋值失败
            orderItemPOS.add(orderItemPO);
        }

        /*final BigDecimal[] totalPriceArray = {BigDecimal.ZERO};
        goodsItemParams.forEach(goodsItem-> {
            //入参的商品id
            Long goodsId = goodsItem.getGoodsId();
            //入参的商品数量
            Integer goodsNum = goodsItem.getGoodsNum();
            //通过入参取当前商品的信息 (价格)
            GoodsPO goodsPO = groupGoods.get(goodsId);
            BigDecimal salePrice = goodsPO.getSalePrice();
            //一种商品的单价 * 一种商品的数量 == 一种商品的总价
            BigDecimal multiply = salePrice.multiply(new BigDecimal(goodsNum));
            totalPriceArray[0] = totalPriceArray[0].add(multiply);
        });*/

        OrderPO orderPO = buildOrderPO(user, totalPrice, goodsNums);
        Integer rows = orderRepository.save(orderPO);
        if (rows != 1) {
            throw new ServiceException(ServiceCodeEnum.INSERT_ERROR);
        }


        orderItemPOS.forEach(orderItemPO -> {
            orderItemPO.setOrderId(orderPO.getId());
            Integer itemRows = orderItemRepository.save(orderItemPO); //建议用批保存方式,购物商品数量较少可以循环保存
            if (itemRows != 1) {
                throw new ServiceException(ServiceCodeEnum.INSERT_ERROR);
            }
        });

    }

    /**
     * 构建订单详情商品列表PO
     *
     * @param goodsNum  商品数量
     * @param goodsId   商品id
     * @param salePrice 商品单价
     * @return OrderItemPO
     */
    private OrderItemPO buildOrderItemPO(Integer goodsNum, Long goodsId, BigDecimal salePrice) {
        OrderItemPO orderItemPO = new OrderItemPO();
        orderItemPO.setGoodsNum(goodsNum);
        orderItemPO.setGoodsId(goodsId);
        orderItemPO.setSaleUnitPrice(salePrice);
        return orderItemPO;
    }

    /**
     * 构建订单表PO
     *
     * @param user       用户信息
     * @param totalPrice 总价格
     * @param goodsNums  商品总数
     * @return OrderPO
     */
    private OrderPO buildOrderPO(CurrentUser user, BigDecimal totalPrice, Integer goodsNums) {
        OrderPO orderPO = new OrderPO();
        orderPO.setOrderNo(UUID.randomUUID().toString());//uuid 后续可以雪花算法
        orderPO.setTotalPrice(totalPrice);
        orderPO.setBuyerId(user.getId());
        orderPO.setGoodsNum(goodsNums);
        orderPO.setOrderState(0); //订单新建状态 常量或者枚举
        return orderPO;
    }

    @Override
    public PageData<OrderListVO> listOrder(CurrentUser user, Integer pageSize, Integer pageNum) {
        //分页查询订单列表
        PageData<OrderPO> orderPOS = orderRepository.pageOrderListByUserId(user.getId(), pageSize, pageNum);//一次数据库查询
        //订单列表分页数据copy到我们自定义的PageData
        PageData<OrderListVO> result = orderPageDaoPO2VO(orderPOS);
        log.debug("订单列表信息-入参:{},出参-{}", user.getId(), orderPOS);
        List<OrderListVO> orderListVOS = new ArrayList<>();
        result.setList(orderListVOS);
        //判断订单列表是否为空
        if (orderPOS != null && !CollectionUtils.isEmpty(orderPOS.getList())) {
            //循环每个订单信息,去获取订单的商品信息
            for (OrderPO orderPO : orderPOS.getList()) {
                OrderListVO orderListVO = orderPO2VO(orderPO);
                orderListVOS.add(orderListVO);
                //通过订单id获取商品列表
                List<OrderItemPO> orderItemPOS = orderItemRepository.listOrderItemByOrderId(orderPO.getId());
                //假定一页10条 10此数据库查询   1 先查订单 + 循环查询 订单列表数量的次数
                // 减少数据查询方案 2次  1 先查订单 2 查所有的商品信息
                //1  orderPOS 订单id全部拿出来 [22,23]
                //2  单个订单id变成订单id列表
                //3  通过多个id查询出所有的商品信息
                //4  给商品信息进行分组 按照订单id <订单id,List<商品信息>>
                //5  PO转为VO  循环的时候 把 orderPO 转换为 OrderListVO
                //6 通过orderPO的订单id 去map里查 返回商品列表
                //7 循环商品列表 商品的PO转换为商品的VO
                log.debug("订单列表商品信息-入参:{},出参-{}", orderPO.getId(), orderItemPOS);
                List<OrderGoodsItemVO> orderGoodsItemVOS = new ArrayList<>();
                //判断订单下是否有商品列表
                if (!CollectionUtils.isEmpty(orderItemPOS)) {
                    //循环商品列表 把商品PO转换为商品VO
                    for (OrderItemPO orderItemPO : orderItemPOS) {
                        orderGoodsItemVOS.add(orderItemPO2VO(orderItemPO));
                    }
                }
                orderListVO.setGoodsItemVOS(orderGoodsItemVOS);
            }
            //取出来的po转换为vo
        } else {
            //不抛异常 订单列表为空
        }
        return result;
    }

    private PageData<OrderListVO> orderPageDaoPO2VO(PageData<OrderPO> orderPOS) {
        PageData<OrderListVO> orderListVOPageData = new PageData<>();
        BeanUtils.copyProperties(orderPOS, orderListVOPageData);
        return orderListVOPageData;
    }

    private OrderListVO orderPO2VO(OrderPO orderPO) {
        OrderListVO orderListVO = new OrderListVO();
        BeanUtils.copyProperties(orderPO, orderListVO);
        return orderListVO;
    }

    private OrderGoodsItemVO orderItemPO2VO(OrderItemPO orderItemPO) {
        OrderGoodsItemVO orderGoodsItemVO = new OrderGoodsItemVO();
        BeanUtils.copyProperties(orderItemPO, orderGoodsItemVO);
        return orderGoodsItemVO;
    }
}
